#include "../build/include/ly/AudioPlayer.h"
#include <InteractApp.hpp>
#include "AudioPlayer.hpp"

#include "kissfft/kiss_fft.h"
#include "kissfft/kiss_fftr.h"




JNIEXPORT jstring JNICALL Java_ly_AudioPlayer__1getErrorString
  (JNIEnv *env, jobject obj, jlong ptr)
{
    AudioPlayer *nativePlayer = (AudioPlayer *)ptr;

    char *des = NULL;
    nativePlayer->getErrorInfo(&des);
    if (des == NULL) return NULL;
    return (*env)->NewStringUTF(env, des);
}

#include <stdio.h>
static void write_file(const char *name, void *data, int len)
{
    FILE *bin_fp = fopen(name, "wb");
    if (!bin_fp) {
        perror("无法创建二进制文件");
        exit(1);
    }
    // 写入频谱数据（float数组）
    fwrite(data, len, 1, bin_fp);

    fclose(bin_fp);

}
JNIEXPORT jlong JNICALL Java_ly_AudioPlayer_nativeCtl
  (JNIEnv *env, jobject obj, jint cmd, jlong ptr, jstring p1, jint p2)
{
    jlong rev = 0;
    AudioPlayer *nativePlayer = (AudioPlayer *)ptr;
    AudioPlayer::State state;
    const char *dom;

    switch (cmd)
    {
    case 1: {
        dom = (*env)->GetStringUTFChars(env, p1, NULL);
        nativePlayer = new AudioPlayer(InteractApp::getGlobal()->getRunLoop(), dom);
        (*env)->ReleaseStringUTFChars (env, p1, dom);
        rev = (jlong)nativePlayer;

        JavaVM *vm;
        (*env)->GetJavaVM(env, &vm);
        jobject weakObj = (*env)->NewWeakGlobalRef(env, obj);

        nativePlayer->stateChangeCB = [vm, weakObj](AudioPlayer *player) {
            JNIEnv *env = NULL;
            (*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6);

            jclass cls = (*env)->GetObjectClass(env, weakObj);
            jmethodID nofid = (*env)->GetMethodID(env, cls, "__nativeListenerSend", "(I)V");

            (*env)->CallVoidMethod(env, weakObj, nofid, (jint)0);
            (*env)->DeleteLocalRef(env, cls);
        };
        nativePlayer->pcmCB = [vm, weakObj](AudioPlayer *player, int16_t *pcm, int32_t sampleCnt) {
#if 1
            // printf("pcm:%p value:%d\n", pcm, sampleCnt);
            if (sampleCnt < 512) return;
            JNIEnv *env = NULL;
            (*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6);

            const AudioPCMInfo *pcminfo = player->getPCMInfo();
            const int FFT_SIZE = sampleCnt/pcminfo->channel;
            kiss_fft_scalar *fft_in = (kiss_fft_scalar*)malloc(FFT_SIZE * sizeof(kiss_fft_scalar));
            kiss_fft_cpx *fft_out = (kiss_fft_cpx*)malloc((FFT_SIZE/2 + 1) * sizeof(kiss_fft_cpx));
            int i;
            for (i = 0; i < sampleCnt; i += pcminfo->channel) {
                fft_in[i / pcminfo->channel] = ((float)pcm[i]) / 32768;
            }
            kiss_fftr_cfg cfg = kiss_fftr_alloc(FFT_SIZE, 0, NULL, NULL);
            // write_file("pcm_input.pcm", pcm, sampleCnt * 2);
            kiss_fftr(cfg, fft_in, fft_out);

            jintArray result = (*env)->NewIntArray(env, FFT_SIZE/2);
            if (result == NULL) {
                return;
            }

            // 获取数组元素的指针
            jint* elements = (*env)->GetIntArrayElements(env, result, NULL);
            if (elements == NULL) {
                return;
            }

            // 填充数据
            for (int i = 0; i < FFT_SIZE/2; i++) {
                // elements[i] = (jbyte)i;
                float v = sqrtf(fft_out[i].r * fft_out[i].r + fft_out[i].i * fft_out[i].i);
                elements[i] = (jbyte)v;
                if (v > 100) {
                    int SAMPLE_RATE = 44100;
                    // printf("i:%d value:%f\n", i* SAMPLE_RATE / FFT_SIZE, v);
                }
            }
            
            jclass cls = (*env)->GetObjectClass(env, weakObj);
            jmethodID nofid = (*env)->GetMethodID(env, cls, "__nativeListenerSendSpectrum", "([II)V");

            (*env)->CallVoidMethod(env, weakObj, nofid, result, (jint)FFT_SIZE/2);
            (*env)->DeleteLocalRef(env, cls);

            // 释放内存，并将更改提交到Java数组
            (*env)->ReleaseIntArrayElements(env, result, elements, 0);

            free(cfg);
            free(fft_in);
            free(fft_out);
#else
#endif
        };
        break;
    }
    case 2:
        state = nativePlayer->getState();
        switch (state)
        {
        case AudioPlayer::State::IDLE:    rev = 0; break;
        case AudioPlayer::State::LOADING: rev = 1; break;
        case AudioPlayer::State::PLAYING: rev = 2; break;
        case AudioPlayer::State::PAUSED:  rev = 3; break;
        default: abort(); break;
        }
        break;
    case 3:
        nativePlayer->seekTo(p2);
        break;
    case 4:
        nativePlayer->load();
        break;
    case 5:
        nativePlayer->resume();
        break;
    case 6:
        nativePlayer->pause();
        break;
    case 7:
        nativePlayer->stop();
        break;
    case 8:
        nativePlayer->destroy();
        break;
    default:
        abort();
        break;
    }
    return rev;
}
